package com.jiegui.payinterface.handler.impl.wxpay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;

import com.jiegui.payinterface.utils.HttpUtil;
import com.jiegui.common.exception.user.RRException;
import com.jiegui.common.utils.AmountUtils;
import com.jiegui.payinterface.bean.PaymentRes;
import com.jiegui.payinterface.enums.PaymentStatus;
import com.jiegui.payinterface.handler.PaymentHandler;
import com.jiegui.payinterface.handler.impl.wxpay.utils.XmlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * 微信支付
 *
 * @author wuqicheng
 * @date 2021/3/10
 */
@Slf4j
@Service("wxpayHandler")
public class WxpayHandlerImpl implements PaymentHandler {

    /**
     * 微信支付
     * 文档详情 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
     *
     *
     * billCode -   订单编号
     * amount   -   交易金额
     * payType  -   支付类型 JSAPI, NATIVE
     * remark   -   商品描述
     * notifyUrl   -   通知地址
     * attach   -   附加数据
     * userId      -   小程序用户userId
     * interfaceCode      -   接口编号
     *
     * tradeConfigs -   交易配置json字符串
     *      payUrl  -   交易地址    "https://api.mch.weixin.qq.com/pay/unifiedorder"
     *      mchId   -   微信支付商户号
     *      mchKey  -   微信支付商户密钥
     *      appid   -   小程序appid
     */
    @Override
    public PaymentRes pay(Map<String, String> map) {
        PaymentRes res = new PaymentRes();
        res.setBillCode(map.get("billCode"));
        Properties properties = JSON.parseObject(map.get("tradeConfigs"), new TypeReference<Properties>() {});

        SortedMap<String, String> resMap =new TreeMap<>();
        resMap.put("sign_type", "MD5");
        resMap.put("spbill_create_ip", "127.0.0.1");
        resMap.put("body", map.get("remark"));
        resMap.put("out_trade_no", map.get("billCode"));
        resMap.put("total_fee", AmountUtils.amountToFen(Double.parseDouble(map.get("amount"))));
        resMap.put("notify_url", map.get("notifyUrl") + "?interfaceCode=" + map.get("interfaceCode"));
        resMap.put("attach", map.get("attach"));
        resMap.put("trade_type", map.get("payType"));
        resMap.put("appid", properties.getProperty("appid"));
        resMap.put("openid", map.get("userId"));
        resMap.put("mch_id", properties.getProperty("mchId"));
        resMap.put("nonce_str", RandomStringUtils.randomAlphanumeric(8));
        log.info("微信支付请求报文: {}", resMap);
        // 请求参数签名
        resMap.put("sign", XmlUtils.createSign(resMap, properties.getProperty("mchKey")));


        try {
            String xmlStr = XmlUtils.parseXML(resMap);
            log.info("微信支付请求报文(XML): {}", xmlStr);
            String resultStr = HttpUtil.request(properties.getProperty("payUrl"), xmlStr, "POST");
            log.info("微信支付响应消息(XML):: {}", resultStr);

            SortedMap<String, String> result = XmlUtils.getMapFromXml(resultStr);
            log.info("微信支付响应消息: {}", result);
            if ("SUCCESS".equals(result.get("result_code")) && "SUCCESS".equals(result.get("result_code"))) {
                // 验证签名
                String sign = XmlUtils.createSign(result, properties.getProperty("mchKey"));
                if (!sign.equals(result.get("sign"))) {
                    res.setStatus(PaymentStatus.FAILURE);
                    res.setResponseCode("999");
                    res.setResponseMessage("签名验证失败");
                    return res;
                }

                if ("JSAPI".equals(map.get("payType"))) {
                    res.remove("billCode");
                    res.put("package", "prepay_id="+ result.get("prepay_id"));
                    res.put("nonceStr", result.get("nonce_str"));
                    res.put("signType", "MD5");
                    res.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
                    res.put("appId", result.get("appid"));
                    res.put("paySign", XmlUtils.createSign(res.toSortMap(), properties.getProperty("mchKey")));
                    res.setBillCode(map.get("billCode"));
                }

                if (StringUtils.equals(map.get("payType"), "NATIVE")) {
                    res.put("code_url", result.get("code_url"));
                }

                res.setStatus(PaymentStatus.SUCCESS);
                res.setResponseCode(result.get("result_code"));
                res.setResponseMessage("交易成功");
            } else {
                res.setStatus(PaymentStatus.FAILURE);
                res.setResponseCode(result.get("err_code"));
                res.setResponseMessage(result.get("err_code_des"));
                log.error("微信支付请求失败: 错误代码={}  请求描述={}  错误描述={}", result.get("err_code"), result.get("return_msg"), result.get("err_code_des"));
            }

        } catch (Exception e) {
            log.error("微信支付请求异常:", e);
            res.setStatus(PaymentStatus.FAILURE);
            res.setResponseMessage("微信支付请求异常");
        }

        return res;
    }

    /**
     * 微信查询
     * 文档详情 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2
     *
     *
     * billCode -   订单编号
     *
     * tradeConfigs -   交易配置json字符串
     *      queryUrl  -   交易地址    "https://api.mch.weixin.qq.com/pay/orderquery"
     *      mchId   -   微信支付商户号
     *      mchKey  -   微信支付商户密钥
     *      appid   -   小程序appid
     */
    @Override
    public PaymentRes query(Map<String, String> map) {
        PaymentRes res = new PaymentRes();
        res.setBillCode(map.get("billCode"));
        Properties properties = JSON.parseObject(map.get("tradeConfigs"), new TypeReference<Properties>() {});

        SortedMap<String, String> resMap =new TreeMap<>();
        resMap.put("sign_type", "MD5");
        resMap.put("out_trade_no", map.get("billCode"));
        resMap.put("appid", properties.getProperty("appid"));
        resMap.put("mch_id", properties.getProperty("mchId"));
        resMap.put("nonce_str", RandomStringUtils.randomAlphanumeric(8));
        log.info("微信查询请求报文: {}", resMap);
        // 请求参数签名
        resMap.put("sign", XmlUtils.createSign(resMap, properties.getProperty("mchKey")));

        try {
            String xmlStr = XmlUtils.parseXML(resMap);
            log.info("微信查询请求报文(XML): {}", xmlStr);
            String resultStr = HttpUtil.request(properties.getProperty("queryUrl"), xmlStr, "POST");
            log.info("微信查询响应消息(XML):: {}", resultStr);

            SortedMap<String, String> result = XmlUtils.getMapFromXml(resultStr);
            log.info("微信查询响应消息: {}", result);
            if ("SUCCESS".equals(result.get("return_code")) && "SUCCESS".equals(result.get("result_code"))) {
                // 验证签名
                String sign = XmlUtils.createSign(result, properties.getProperty("mchKey"));
                if (!sign.equals(result.get("sign"))) {
                    res.setStatus(PaymentStatus.FAILURE);
                    res.setResponseCode("999");
                    res.setResponseMessage("签名验证失败");
                    return res;
                }

                res.putAll(result);
                res.setStatus(PaymentStatus.SUCCESS);
                res.setResponseCode(result.get("result_code"));
                res.setResponseMessage("交易成功");
            } else {
                res.setStatus(PaymentStatus.FAILURE);
                res.setResponseCode(result.get("err_code"));
                res.setResponseMessage(result.get("err_code_des"));
                log.error("微信查询请求失败: 错误代码={}  请求描述={}  错误描述={}", result.get("err_code"), result.get("return_msg"), result.get("err_code_des"));
            }

        } catch (Exception e) {
            log.error("微信查询请求异常:", e);
            res.setStatus(PaymentStatus.FAILURE);
            res.setResponseMessage("微信查询请求异常");
        }

        return res;
    }

    /**
     * 微信退款
     * 文档详情 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
     *
     * billCode -   订单编号
     * refundCode   -   退款订单编号
     * amount   -   交易金额
     * refundAmount -   退款金额
     * notifyUrl   -   通知地址
     *
     * tradeConfigs -   交易配置json字符串
     *      refundUrl  -   退款地址    "https://api.mch.weixin.qq.com/secapi/pay/refund"
     *      mchId   -   微信支付商户号
     *      mchKey  -   微信支付商户密钥
     *      appid   -   小程序appid
     *      mchCertFile -   微信支付商户api证书
     *      mchCertPwd  -   微信支付商户api证书密码
     *
     *
     * @return
     *  transaction_id  -   接口订单编号
     */
    @Override
    public PaymentRes refund(Map<String, String> map) {
        PaymentRes res = new PaymentRes();
        res.setBillCode(map.get("billCode"));
        Properties properties = JSON.parseObject(map.get("tradeConfigs"), new TypeReference<Properties>() {});


        SortedMap<String, String> resMap =new TreeMap<>();
        resMap.put("sign_type", "MD5");
        resMap.put("appid", properties.getProperty("appid"));
        resMap.put("mch_id", properties.getProperty("mchId"));
        resMap.put("nonce_str", RandomStringUtils.randomAlphanumeric(8));
        resMap.put("out_trade_no", map.get("billCode"));
        resMap.put("out_refund_no", map.get("billCode"));

        resMap.put("total_fee", AmountUtils.amountToFen(Double.parseDouble(map.get("amount"))));
        resMap.put("refund_fee", AmountUtils.amountToFen(Double.parseDouble(map.get("refundAmount"))));
        resMap.put("refund_desc", map.get("remark"));
        resMap.put("notify_url", map.get("notifyUrl"));

        log.info("微信退款请求报文: {}", resMap);
        // 请求参数签名
        resMap.put("sign", XmlUtils.createSign(resMap, properties.getProperty("mchKey")));


        try {
            String xmlStr = XmlUtils.parseXML(resMap);
            log.info("微信退款请求报文(XML): {}", xmlStr);
            String resultStr = HttpUtil.reqeuestWichSSL(properties.getProperty("refundUrl"), xmlStr, properties.getProperty("mchCertFile"), properties.getProperty("mchCertPwd"));
            log.info("微信退款响应消息(XML):: {}", resultStr);

            SortedMap<String, String> result = XmlUtils.getMapFromXml(resultStr);
            log.info("微信退款响应消息: {}", result);
            if ("SUCCESS".equals(result.get("result_code")) && "SUCCESS".equals(result.get("result_code"))) {
                // 验证签名
                String sign = XmlUtils.createSign(result, properties.getProperty("mchKey"));
                if (!sign.equals(result.get("sign"))) {
                    res.setStatus(PaymentStatus.FAILURE);
                    res.setResponseCode("999");
                    res.setResponseMessage("签名验证失败");
                    return res;
                }
                res.setStatus(PaymentStatus.SUCCESS);
                res.setResponseCode(result.get("result_code"));
                res.setResponseMessage("退款成功");
            } else {
                res.setStatus(PaymentStatus.FAILURE);
                res.setResponseCode(result.get("err_code"));
                res.setResponseMessage(result.get("err_code_des"));
                log.error("微信退款请求失败: 错误代码={}  请求描述={}  错误描述={}", result.get("err_code"), result.get("return_msg"), result.get("err_code_des"));
            }

        } catch (Exception e) {
            log.error("微信退款请求异常:", e);
            res.setStatus(PaymentStatus.FAILURE);
            res.setResponseMessage("微信退款请求异常");
        }

        return res;
    }

    /**
     * 企业付款到零钱
     */
    public PaymentRes drawcash(Map<String, String> map) {
        PaymentRes res = new PaymentRes();
        String payUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
        // 组装请求报文
        SortedMap<String, String> resMap = new TreeMap<>();
        resMap.put("mch_appid", map.get("appid"));
        resMap.put("mchid", map.get("mchid"));
        resMap.put("partner_trade_no", map.get("billCode"));
        resMap.put("openid", map.get("openid"));
        resMap.put("check_name", "NO_CHECK");
        resMap.put("amount", AmountUtils.amountToFen(Double.parseDouble(map.get("amount"))));
        resMap.put("desc", map.get("desc"));
        resMap.put("spbill_create_ip", map.get("ip"));
        resMap.put("nonce_str", XmlUtils.getNonceStr());
        res.setBillCode(resMap.get("partner_trade_no"));
        log.info("微信付款到零钱请求报文: {}", resMap);

        // 签名
        resMap.put("sign", XmlUtils.createSign(resMap, map.get("key")));

        try {
            //转换XML
            String xmlStr = XmlUtils.parseXML(resMap);
            log.info("微信付款到零钱请求报文(XML): {}", xmlStr);
            String resultStr = HttpUtil.reqeuestWichSSL(payUrl, xmlStr, map.get("certPath"), map.get("certPwd"));
            log.info("微信付款到零钱响应报文(XML):: {}", resultStr);
            SortedMap<String, String> result = XmlUtils.getMapFromXml(resultStr);
            log.info("微信付款到零钱响应报文:: {}", result);

            if ("SUCCESS".equals(result.get("return_code")) && "SUCCESS".equals(result.get("result_code"))) {
                res.putAll(result);
                res.setBillCode(result.get("partner_trade_no"));

                res.setStatus(PaymentStatus.SUCCESS);
                res.setResponseCode(result.get("result_code"));
                res.setResponseMessage("交易成功");
            } else {
                res.setStatus(PaymentStatus.FAILURE);
                res.setResponseCode(result.get("err_code"));
                res.setResponseMessage(result.get("err_code_des"));
                log.error("微信付款到零钱请求: 错误代码={}  请求描述={}  错误描述={}", result.get("err_code"), result.get("return_msg"), result.get("err_code_des"));
            }
        } catch (Exception e) {
            log.error("微信付款到零钱请求异常:", e);
            res.setStatus(PaymentStatus.FAILURE);
            res.setResponseMessage("微信付款到零钱请求异常");
        }

        return res;
    }


    /**
     * 企业付款到零钱状态查询
     */
    public PaymentRes queryDrawCash(Map<String, String> map) throws Exception {
        PaymentRes res = new PaymentRes();
        String payUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo";
        // 组装请求报文
        SortedMap<String, String> resMap = new TreeMap<>();
        resMap.put("partner_trade_no", map.get("partner_trade_no"));
        resMap.put("mch_id", map.get("mchid"));
        resMap.put("partner_trade_no", map.get("partner_trade_no"));
        resMap.put("appid", map.get("appid"));
        resMap.put("nonce_str", XmlUtils.getNonceStr());
        res.setBillCode(resMap.get("partner_trade_no"));
        log.info("微信付款到零钱请求报文: {}", resMap);
        // 签名
        resMap.put("sign", XmlUtils.createSign(resMap, map.get("key")));
        try {
            //转换XML
            String xmlStr = XmlUtils.parseXML(resMap);
            log.info("微信付款到零钱查询报文(XML): {}", xmlStr);
            String resultStr = HttpUtil.reqeuestWichSSL(payUrl, xmlStr, map.get("certPath"), map.get("certPwd"));
            log.info("微信付款到零钱查询响应报文(XML):: {}", resultStr);
            SortedMap<String, String> result = XmlUtils.getMapFromXml(resultStr);
            log.info("微信付款到零钱查询响应报文:: {}", result);

            if ("SUCCESS".equals(result.get("return_code")) && "SUCCESS".equals(result.get("result_code"))&& ("SUCCESS".equals(result.get("status"))||"PROCESSING".equals(result.get("status")))) {
                res.putAll(result);
                res.setBillCode(result.get("partner_trade_no"));
                res.setStatus(PaymentStatus.SUCCESS);
                res.setResponseCode(result.get("result_code"));
                res.setResponseMessage("查询成功");
            } else {
                res.setStatus(PaymentStatus.FAILURE);
                res.setResponseCode(result.get("err_code"));
                res.setResponseMessage(result.get("err_code_des"));
                log.error("微信付款到零钱查询请求: 错误代码={}  请求描述={}  错误描述={}", result.get("err_code"), result.get("return_msg"), result.get("err_code_des"));
                throw new RRException("查询付款结果为失败");
            }
        } catch (Exception e) {
            log.error("微信付款到零钱查询请求异常:", e);
            res.setStatus(PaymentStatus.FAILURE);
            res.setResponseMessage("微信付款到零钱查询请求异常");
            if (e instanceof  RRException){
                throw e;
            }
        }

        return res;
    }

}
